"""
    TagRate API handler
    
    @author: Jean-Lou Dupont
"""

import os
import sys
import logging
import string

from string import Template
from datetime import datetime

import wsgiref.handlers
from google.appengine.ext import webapp
from google.appengine.api import users
from google.appengine.ext import db

from libs.user.prefs    import UserPrefs
from libs.tagrate.post  import TagRatePost

class TagRatePostApiHandler(webapp.RequestHandler):

    def post(self):
        """
            Add a 'post' to the user's list
        """
        
        try:
            title  = self.request.get('title')
            url    = self.request.get('url')
            notes  = self.request.get('notes')
            rating = self.request.get('rating')
        except Exception, e:
            self.response.set_status( 500 )
            self.response.out.write( "{ 'code':'500', 'msg':'%s' }" % e.message )
            logging.info("TagRatePostApi.post, EXCEPTION raised, msg=%s" % e)
            return
        
        user = users.get_current_user()
        if not user:
            self.response.set_status( 401 )
            self.response.out.write( "{ 'code':'401', 'msg':'login required' }" )
            return
        
        try:
            p = TagRatePost.create( user )
        except:
            p = None
        
        if not p:
            self.response.set_status( 500 )
            self.response.out.write( "{ 'code':'500', 'msg':'cannot create TagRatePost' }" )
            return
        
        p.title  = title
        p.url    = url
        p.notes  = db.Blob( str( notes ) )
        p.rating = rating
        
        try:
            p.put()
            self.response.set_status( 200 )
            self.response.out.write( "{ 'code':'200' }" )
            logging.info("TagRatePostApi.post, modified[%s] url[%s] title[%s] rating[%s]" % (p.modified, url, title, rating) )            
        except:
            self.response.set_status( 500 )
            self.response.out.write( "{ 'code':'500', 'msg':'cannot put TagRatePost' }" )
            
        #Can't set Content-Type to json because GWT FormHandler will bark...
        #self.response.headers['Content-Type'] = "application/json"
        
               
class TagRatePostDefaultApiHandler(webapp.RequestHandler):
    
    def get(self, uid):
        """
            Retrieves the 'default' feed
            " /api/tagrate/(.*?)/post/default "
            i.e. the last 10 entries for the user 'uid'
            
            NOTE: GoogleReader seems to have a limit in terms of size of feed 
                  it will handle...
        """
        up = UserPrefs.fromUid( uid )
        if not up:
            self.response.set_status( 400 ) #BAD UID
            self.response.out.write( "UNRECOGNIZED UID" )
            return

        user_agent = self.request.headers['User-Agent']
        
        #Revalidation call?
        #==================
        try:    
                if_modified_since = self.request.headers['If-Modified-Since']
                #logging.info( 'TagRatePostDefaultApiHandler.get, if-modified-since[%s]' %   if_modified_since )
        except: if_modified_since = None
        
        try:    
                if_none_match     = self.request.headers['If-None-Match']
                #logging.info( 'TagRatePostDefaultApiHandler.get, if-none-match[%s]' %       if_none_match )
        except: if_none_match     = None        

        logging.info( 'TagRatePostDefaultApiHandler.get, user[%s]' %        str( up.user) )
        #logging.info( 'TagRatePostDefaultApiHandler.get, UserAgent[%s]' %   user_agent )
        
        if ( if_none_match ):
            logging.info( 'TagRatePostDefaultApiHandler.get, REVALIDATION' )
            self.handleRevalidation( up, if_none_match )
        else:
            self.handleFullFetch( up )

    def handleRevalidation(self, userPrefs, if_none_match ):
        last_entry = TagRatePost.defaultList( userPrefs.user, 1 )
        if last_entry:
            etag = '"' + str(last_entry[0].modified) + '"'
            if etag == if_none_match:
                self.response.set_status( 304 ) #not-modified
                return
        
        return self.handleFullFetch( userPrefs )

    def handleFullFetch(self, userPrefs):
       
        try:
            (channel, last_modified) = self.generateAtomFeed( userPrefs )
            
            self.response.out.write( channel )
            self.response.set_status( 200 )
            self.response.headers['Content-Type']   = 'application/atom+xml'
            self.response.headers['Cache-Control']  = 'must-revalidate'
            
            if last_modified:
                etag = '"'+str( last_modified.modified )+'"'
                self.response.headers['ETag'] = etag
            else: 
                etag = ''
                     
        except Exception, e:
            logging.error('TagRatePostDefaultApiHandler.handleFullFetch: EXCEPTION raised, msg='+e.message)
            self.response.set_status( 500 ) 
            return
        
        logging.info( 'TagRatePostDefaultApiHandler.get, FULLFETCH, uid[%s] etag[%s]' % (userPrefs.uid, etag))

        
    def generateAtomFeed(self, userPrefs):
        
        user = userPrefs.user
        
        (liste, entries) = self.generateAtomEntryList( user )
        
        uid = userPrefs.uid
        link = 'http://www.web-bloks.com/api/tagrate/%s/post/default' % uid
        
        params = { 
            "title":    user.nickname()+' - TagRate Feed',
            "link":     link,  #TODO
            "feedlink": link,
            "updated":  "",    #TODO last generated...
            "author":   user.nickname(),
            "id":       link,
            "entries":  entries
        }
        channel = self.generateAtomChannel(params)
        
        if liste:
            last_modified = liste[0]
        else:
            last_modified = None
        
        return ( str( channel ), last_modified )

    def generateAtomChannel(self, params):
        tpl = Template( _AtomFeedChannel )
        return str( tpl.substitute( params ) )
        
    def generateAtomEntryList(self, user ):
        """
            DateTime entries must be formatted
            according to RFC3339 using  $date T $time
        """
        #========== FEED ======= POST ======
        _mapper = {
                    'title':    'title',
                    'link' :    'url',
                    'updated':  'modified',
                    'published':'created',
                    'content':  'notes'
                    #'id':       'key'
        }
        _dates  = {
                   'updated':   True,
                   'published': True
        }
        
        liste = TagRatePost.defaultList( user )
        if not liste:
            return ''

        #logging.info('generateAtomEntryList: list AVAILABLE')
        entries = ''
        tpl = Template( _AtomFeedEntry )
        for entry in liste:
            #tpl = Template( _AtomFeedEntry )
            params = {}
            for inFeedKey in _mapper:
                inPostKey = _mapper[ inFeedKey ]
                
                try:    value = getattr( entry , inPostKey )
                except: value = ''
                    
                try:    isDateTime = _dates[inFeedKey]
                except: isDateTime = False
                    
                if isDateTime is True:
                    value = self.formatToRFC3339_DateTime( value )
                    
                params[inFeedKey] = value
                
            params['id'] = entry.key()
            entries += tpl.substitute( params )
        
        return (liste, entries)
        
    def formatToRFC3339_DateTime(self, input):
        """
            example: 2008-09-10T09:44:23.995-07:00
            @todo fix timezone...
        """
        date = str( input.date() )
        time = str( input.time() )
        return date+'T'+time+'-00:00'   
        
#========================================================================================================

#@see http://tools.ietf.org/html/rfc4287
_AtomFeedChannel = """<?xml version="1.0" encoding="utf-8"?>
   <feed xmlns="http://www.w3.org/2005/Atom">

     <title>$title</title>
     <link href="$link"/>
     <link rel="self" type="application/atom+xml" href="$feedlink"/>
     <updated>$updated</updated>
     <author>
       <name>$author</name>
     </author>
     <id>$id</id>

    $entries
    
   </feed>
"""

_AtomFeedEntry = """
    <entry>
       <title>$title</title>
       <id>$id</id>
       <link rel='alternate' type='text/html' href='$link'/>
       <updated>$updated</updated>
       <published>$updated</published>
       <content type="html">
       $content
       </content>
     </entry>
"""


#========================================================================================================
_RssFeedChannel= """
<rss version="2.0"> 
   <channel> 
      <title>$title</title> 
      <link>$link</link> 
      <description>$description</description> 
      <pubDate>$pubdate</pubDate> 
      <lastBuildDate>$lastbuilddate</lastBuildDate> 
      <generator>http://www.web-bloks.com/</generator> 
      $items
   </channel> 
</rss>
"""

_RssFeedEntry = """
      <item> 
         <title>$title</title> 
         <link>$link</link> 
         <description>$description</description> 
         <pubDate>$pubdate</pubDate> 
         <guid>$guid</guid> 
      </item> 
"""               
               
               
#========================================================================================================

__URLMAP = [    ('/api/tagrate/post/',                  TagRatePostApiHandler),
                ('/api/tagrate/(.*?)/post/default',     TagRatePostDefaultApiHandler)
            ]

def main():
  application = webapp.WSGIApplication( __URLMAP, debug=True)
  wsgiref.handlers.CGIHandler().run(application)

if __name__ == "__main__":
    main()        